package com.example.easycharge.service;

import com.alibaba.fastjson.JSONObject;
import com.example.easycharge.entity.*;
import com.example.easycharge.error.*;
import com.example.easycharge.notRepEntity.*;
import com.example.easycharge.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class ChargeService {
    // 当前排到的号，快慢充采用统一的顺序号
    private int nowOrderNumber = 0;
    // 平均充电前延时时间
    private int avgPreMinutes = 5;
    // 平均充电后驶离时间
    private int avgAfterMinutes = 5;

    // 为了降低代码复杂度，空位数只在单次调度中使用，每次单次调度结束后重新计算空位数
    private int emptySize;

    private int MAX_QUEUE_LENGTH = 2;
    private int MAX_AREA_SIZE = 10;
    private int MIN_SIG_SIZE = 5;

    private BaseStrategyEnum baseStrategyEnum = BaseStrategyEnum.SINGLE;
    private ErrorStrategyEnum errorStrategyEnum = ErrorStrategyEnum.PRIORITY;

    private final HashMap<Long, Long> pileToQueue = new HashMap<>();
    private final HashMap<Long, WaitQueue> waitQueue = new HashMap<>();
    private final LinkedList<ChargeRequest> errorList = new LinkedList<>();
    private final WaitArea waitArea = WaitArea.getInstance();
    // 批量调度队列
    private final LinkedList<ChargeRequest> batchList = new LinkedList<>();

    @Autowired
    private CustomerRepository customerRepository;
    @Autowired
    private VehicleRepository vehicleRepository;
    @Autowired
    private MessageItemRepository messageItemRepository;
    @Autowired
    private ChargePileRepository chargePileRepository;
    @Autowired
    private AlertRepository alertRepository;
    @Autowired
    private BillService billService;

    public JSONObject beforeRequest(long customerId, ChargeRequest chargeRequest) throws VehicleNotFoundError, ReChargingError, PileNotFoundError, ServiceNotSupport, OverChargeError, BillNotFoundError, WithoutAuthorityError, QueueNotFoundError, InvalidOperateError {
        System.out.println("已接收充电请求：" + chargeRequest.getVehicleId());
        JSONObject returnJson = new JSONObject();
        long vehicleId = chargeRequest.getVehicleId();
        if (!vehicleRepository.existsById(vehicleId)) {
            throw new VehicleNotFoundError();
        }

        Vehicle vehicle = vehicleRepository.findVehicleByVehicleId(vehicleId);
        if (vehicle.isCharging()) {
            // 车辆已经在充电了，不能反复冲（确实）
            throw new ReChargingError();
        }
        if (!waitArea.inArea(chargeRequest)) {
            returnJson.put("error", "车辆不在允许坐标范围");
            return returnJson;
        }

        chargeRequest.setOriginLeftEnergy(chargeRequest.getLeftEnergy());
        chargeRequest.setTotalEnergy(vehicle.getTotalEnergy());
        Customer customer = customerRepository.findCustomerByUserId(customerId);
        // 设置用户编号
        chargeRequest.setCustomerId(customerId);
        // 设置车辆接受充电功率
        chargeRequest.setChargePower(vehicle.getChargePower());
        chargeRequest.setAutoFix(customer.isAutoFix());
        // 计算电量百分比
        chargeRequest.setBatteryPercent(chargeRequest.getLeftEnergy() / chargeRequest.getTotalEnergy());
        double emptyEnergy = chargeRequest.getTotalEnergy() - chargeRequest.getLeftEnergy();
        if (emptyEnergy < 0) {
            throw new OverChargeError();
        }
        chargeRequest.setRequestEnergy(Math.min(chargeRequest.getRequestEnergy(), emptyEnergy));

        long pileId = chargeRequest.getSelectPileId();
        int orderNum = getOrder();
        // 设置排队编号
        chargeRequest.setOrderNumber(orderNum);
        returnJson = receiveRequest(chargeRequest);
        if (returnJson.containsKey("error")) {
            return returnJson;
        }
        // 将车辆设置为已充电
        vehicleRepository.setVehicleState(true, vehicleId);
        returnJson.put("result", "申请充电成功");
        returnJson.put("orderNum", orderNum);
        return returnJson;
    }

    public JSONObject receiveRequest(ChargeRequest chargeRequest) throws PileNotFoundError, ServiceNotSupport, InvalidOperateError, BillNotFoundError, WithoutAuthorityError, QueueNotFoundError {
        JSONObject jsonObject = new JSONObject();
        if (waitQueue.size() == 0) {
            jsonObject.put("error", "没有足够充电桩");
            return jsonObject;
        }
        switch (baseStrategyEnum) {
            case SINGLE: {
                // 单次调度（默认）
                WaitQueue shortestQueue = findShortestQueue(chargeRequest.isFastCharge());
                if (shortestQueue == null) {
                    if (waitArea.getAreaSize() < MAX_AREA_SIZE) {
                        waitArea.addNewRequestToArea(chargeRequest);
                        jsonObject.put("message", "无空余充电队列，已将您添加至等候区中");
                    } else {
                        jsonObject.put("message", "等待区已无空位，申请充电失败");
                    }
                } else {
                    // 修改充电请求中的选择充电桩为最短充电桩
                    chargeRequest.setSelectPileId(shortestQueue.getPileID());
                    addVehicleToPile(chargeRequest);
                    jsonObject.put("message", "存在空余充电队列，已将您添加至此队列中");
                    jsonObject.put("detail", chargeRequest);
                    // TODO: 2022-06-15 二次封装
                    if (shortestQueue.getChargingVehicle().getVehicleId() == chargeRequest.getVehicleId()) {
                        driveIn(chargeRequest.getVehicleId());
                        plugInPile(chargeRequest.getVehicleId());
                    }
                    // end
                }
                return jsonObject;
            }
            case MULTIPLE: {
                /* 一次调度多辆车
                    要求的是总的调度时间最短，那么就得按照预计充电时间来排序
                    由于预计充电时间和充电桩相关（充电功率），因此按照所需电量来倒序排序
                 */
                waitArea.addNewRequestToArea(chargeRequest);
                // 先判断一下是否达到最小调度标准
                if (waitArea.getAreaSize() > MIN_SIG_SIZE) {
                    int maxLength = MAX_QUEUE_LENGTH;
                    // 获取快充和慢充类型充电桩空位数
                    int fastEmptySize = 0;
                    int slowEmptySize = 0;
                    // 反向对比大根堆，即形成小根堆
                    PriorityQueue<WaitQueue> fastPriQueue = new PriorityQueue<>((o1, o2) -> o2.getWaitMinutes() - o1.getWaitMinutes());
                    PriorityQueue<WaitQueue> slowPriQueue = new PriorityQueue<>((o1, o2) -> o2.getWaitMinutes() - o1.getWaitMinutes());

                    for (WaitQueue queue : waitQueue.values()) {
                        if (queue.getWaitLength() < maxLength) {
                            if (queue.isFast()) {
                                fastPriQueue.add(queue);
                                fastEmptySize += (maxLength - queue.getWaitLength());
                            } else {
                                slowPriQueue.add(queue);
                                slowEmptySize += (maxLength - queue.getWaitLength());
                            }

                        }
                    }

                    LinkedList<ChargeRequest> fastRequestList = new LinkedList<>();
                    LinkedList<ChargeRequest> slowRequestList = new LinkedList<>();
                    while (fastEmptySize > 0) {
                        ChargeRequest topRequest = waitArea.getNextRequest(true);
                        if (topRequest == null) {
                            break;
                        }
                        fastEmptySize--;
                        fastRequestList.add(topRequest);
                    }
                    while (slowEmptySize > 0) {
                        ChargeRequest topRequest = waitArea.getNextRequest(false);
                        if (topRequest == null) {
                            break;
                        }
                        slowEmptySize--;
                        slowRequestList.add(topRequest);
                    }
                    // 把车辆按照预计充电量排序
                    fastRequestList.sort(Comparator.comparingInt(o -> (int) (o.getTotalEnergy() - o.getLeftEnergy())));
                    slowRequestList.sort(Comparator.comparingInt(o -> (int) (o.getTotalEnergy() - o.getLeftEnergy())));

                    // 利用堆来进行排序，如果新车进入后没有空位的话就将其从堆中移出
                    while (!fastRequestList.isEmpty()) {
                        ChargeRequest targetRequest = fastRequestList.pollFirst();
                        WaitQueue targetQueue = fastPriQueue.poll();
                        targetRequest.setSelectPileId(targetQueue.getPileID());
                        addVehicleToPile(targetRequest);
                        if (!targetQueue.isFull()) {
                            fastPriQueue.add(targetQueue);
                        }
                    }
                    while (!slowRequestList.isEmpty()) {
                        ChargeRequest targetRequest = slowRequestList.pollFirst();
                        WaitQueue targetQueue = slowPriQueue.poll();
                        targetRequest.setSelectPileId(targetQueue.getPileID());
                        addVehicleToPile(targetRequest);
                        if (!targetQueue.isFull()) {
                            slowPriQueue.add(targetQueue);
                        }
                    }
                    jsonObject.put("message", "已启动调度，请您查看消息");
                } else {
                    jsonObject.put("message", "等候区数量未达最低调度标准，请等候");
                }
                return jsonObject;
            }
            case BATCH: {
                // 批量调度
                // 车位总数
                int vehicleSpace = MAX_AREA_SIZE + MAX_QUEUE_LENGTH * waitQueue.size();
                if (batchList.size() < vehicleSpace) {
                    batchList.add(chargeRequest);
                    jsonObject.put("message", "车辆数未达调度限额，请等待");
                } else {
                    for (WaitQueue queue : waitQueue.values()) {
                        if (queue.getChargingVehicle() != null) {
                            jsonObject.put("message", "前方车辆仍在充电，请稍后");
                            return jsonObject;
                        }
                    }
                    // 如果所有车辆都完成了充电
                    // （明显有bug，这种情况下如果没有新车驶入的话，大家都没法充电了，但是不管它，懒得改）
                    // 测试用例得精心设计才行
                    // 对充电请求按照申请电量排序
                    batchList.sort((o1, o2) -> (int) (o1.getRequestEnergy() - o2.getRequestEnergy()));
                    // TODO: 2022-06-18 设置
                }
                return jsonObject;
            }
            case SELECT: {
                // 用户选择（原始）,无限大的等候队列，无等候区
                if (chargeRequest.getSelectPileId() == 0) {
                    return autoSelectPile(chargeRequest);
                } else {
                    addVehicleToPile(chargeRequest);
                    // TODO: 2022-05-27 返回值
                }
            }
        }
        return null;
    }

    public void addVehicleToPile(ChargeRequest chargeRequest) throws PileNotFoundError, ServiceNotSupport {
        long pileId = chargeRequest.getSelectPileId();

        if (!pileToQueue.containsKey(pileId)) {
            throw new PileNotFoundError();
        }
        ChargePile chargePile = chargePileRepository.findChargePileByChargePileId(pileId);
        // 临时修改，出现了bug，数据库缓存不一致，先注释掉这个

//        if (!chargePile.isActive() || chargePile.isError()) {
//            throw new ServiceNotSupport();
//        }

        long queueId = pileToQueue.get(pileId);
        // 设定预计充电所需时间
        int chargePower = Math.min(chargePile.getChargePower(), chargeRequest.getChargePower());
        int chargeTime = (int) (chargeRequest.getRequestEnergy() / chargePower * 60);
        chargeRequest.setEstimateChargeTime(chargeTime);
        chargeRequest.setRealChargePower(chargePower);

        waitQueue.get(queueId).addNewVehicle(chargeRequest, chargeTime);
        // 开始记账
        billService.startBill(chargeRequest.getVehicleId(), pileId, chargePower);
    }

    // 自动选择最短的队列
    public JSONObject autoSelectPile(ChargeRequest chargeRequest) throws PileNotFoundError, ServiceNotSupport {
        JSONObject returnJson = new JSONObject();
        WaitQueue shortestQueue = findShortestQueue(chargeRequest.isFastCharge());
        long pileId = shortestQueue.getPileID();
        // 找到最近的充电桩之后加入队列中
        chargeRequest.setSelectPileId(pileId);
        addVehicleToPile(chargeRequest);
        returnJson.put("queueDetail", shortestQueue);
        return returnJson;
    }

    public void addNewQueue(ChargePile pile, long queueId) {
        long pileId = pile.getChargePileId();
        WaitQueue newQueue = new WaitQueue(pileId, pile.getChargePower(), pile.isFast());
        waitQueue.put(queueId, newQueue);
        pileToQueue.put(pileId, queueId);
    }

    public void removeQueue(long pileId) {
        waitQueue.remove(pileToQueue.get(pileId));
        pileToQueue.remove(pileId);
    }

    public void transVehicle(long pileId) throws PileNotFoundError, ServiceNotSupport {
        long queueId = pileToQueue.get(pileId);
        WaitQueue removeQueue = waitQueue.get(queueId);
        if (removeQueue.getChargingVehicle() == null) {
            return;
        }
        // 将这个队列中的车辆依次移动到其他队列中
        switch (errorStrategyEnum) {
            case PRIORITY: {
                // 先不从等候区叫号，而是从故障充电桩等待队列中叫号
                LinkedList<ChargeRequest> errorPileList = removeQueue.getChargeRequestLinkedList();
                ChargeRequest chargingVehicle = removeQueue.getChargingVehicle();
                // 重新设定请求电量
                chargingVehicle.setRequestEnergy(chargingVehicle.getRequestEnergy() + chargingVehicle.getOriginLeftEnergy() - chargingVehicle.getLeftEnergy());
                errorPileList.add(0, chargingVehicle);
                // 构建堆
                PriorityQueue<WaitQueue> priorityQueue = new PriorityQueue<>((o1, o2) -> o2.getWaitMinutes() - o1.getWaitMinutes());
                for (WaitQueue waitQueue : waitQueue.values()) {
                    // 只把同一类型的充电桩加入进来
                    if (waitQueue != removeQueue && (waitQueue.isFast()) == removeQueue.isFast() && !waitQueue.isFull()) {
                        priorityQueue.add(waitQueue);
                    }
                }
                while (priorityQueue.size() != 0 && errorPileList.size() != 0) {
                    WaitQueue shortQueue = priorityQueue.poll();
                    ChargeRequest chargeRequest = errorPileList.poll();
                    chargeRequest.setSelectPileId(shortQueue.getPileID());
                    addVehicleToPile(chargeRequest);
                    if (!shortQueue.isFull()) {
                        priorityQueue.add(shortQueue);
                    }
                }
                if (errorPileList.size() != 0)
                    errorList.addAll(errorPileList);
                break;
            }
            case SCHEDULE: {
                // 与其他充电桩等待车辆合为一组，重新安排调度
                LinkedList<ChargeRequest> scheduleList = new LinkedList<>();

                for (WaitQueue waitQueue : waitQueue.values()) {
                    // 只把同一类型的充电桩加入进来
                    if (waitQueue != removeQueue && (waitQueue.isFast()) == removeQueue.isFast()) {
                        scheduleList.addAll(waitQueue.removeWaitVehicle());
                    }
                }
                // 按照时间顺序排序
                scheduleList.sort(Comparator.comparingInt(ChargeRequest::getOrderNumber));
                int leftSpace = (waitQueue.size() - 1) * MAX_QUEUE_LENGTH;
                while (scheduleList.size() > 0 && leftSpace > 0) {
                    for (WaitQueue targetQueue : waitQueue.values()) {
                        if (targetQueue.isFast() != removeQueue.isFast()) {
                            continue;
                        }
                        ChargeRequest movedRequest = scheduleList.pollFirst();
                        movedRequest.setSelectPileId(targetQueue.getPileID());
                        addVehicleToPile(movedRequest);
                        leftSpace--;
                        MessageItem messageItem = new MessageItem(movedRequest.getCustomerId(), "您的车辆已被移动至" + targetQueue.getPileID() + "号充电桩");
                        if (scheduleList.size() == 0 || leftSpace == 0) {
                            break;
                        }
                    }
                }
                // 把多余的车辆都给移到等候区中
                while (scheduleList.size() > 0) {
                    ChargeRequest tailRequest = scheduleList.pollLast();
                    waitArea.insertRequest(tailRequest);
                    MessageItem messageItem = new MessageItem(tailRequest.getCustomerId(), "因队列调整，您的车辆已被移至等候区等待");
                    messageItemRepository.saveAndFlush(messageItem);
                }
                break;
            }
            case TAIL: {
                for (ChargeRequest chargeRequest : removeQueue.getChargeRequestLinkedList()) {
                    MessageItem messageItem;
                    if (chargeRequest.isAutoFix()) {
                        autoSelectPile(chargeRequest);
                        // 发出通知
                        messageItem = new MessageItem(chargeRequest.getCustomerId(), "您的充电请求已被移至其他充电桩");
                    } else {
                        // 发出通知
                        messageItem = new MessageItem(chargeRequest.getCustomerId(), "您的充电请求被迫停止，请您及时选择其他充电桩");
                    }
                    messageItemRepository.saveAndFlush(messageItem);
                }
                break;
            }
        }
        // 清除原先队列中的车辆
        removeQueue.getChargeRequestLinkedList().clear();
    }

    public void recoveryQueue(long pileId) throws PileNotFoundError, ServiceNotSupport, InvalidOperateError, BillNotFoundError, WithoutAuthorityError, QueueNotFoundError {
        // 恢复队列，合为一组重新调度
        WaitQueue recoveryQueue = waitQueue.get(pileToQueue.get(pileId));
        boolean isFast = recoveryQueue.isFast();
        LinkedList<ChargeRequest> recoveryList = new LinkedList<>();
        for (WaitQueue waitQueue : waitQueue.values()) {
            if (waitQueue.isFast() == isFast && recoveryQueue.getPileID() != waitQueue.getPileID() && waitQueue.getWaitLength() != 0) {
                recoveryList.addAll(waitQueue.removeWaitVehicle());
            }
        }
        recoveryList.sort(Comparator.comparingInt(ChargeRequest::getOrderNumber));
        ChargeRequest topRequest = recoveryList.pollFirst();
        topRequest.setSelectPileId(pileId);
        addVehicleToPile(topRequest);
        // TODO: 2022-06-18 临时修改
        driveIn(topRequest.getVehicleId());
        plugInPile(topRequest.getVehicleId());
        while (recoveryList.size() > 0) {
            for (WaitQueue target : waitQueue.values()) {
                if (target.isFast() == isFast) {
                    if (recoveryList.size() == 0) {
                        return;
                    }
                    ChargeRequest reSortRequest = recoveryList.pollFirst();
                    reSortRequest.setSelectPileId(target.getPileID());
                    addVehicleToPile(reSortRequest);
                    // 发送信息
                    MessageItem message = new MessageItem(reSortRequest.getCustomerId(), "您的车辆已被重新调度");
                    messageItemRepository.saveAndFlush(message);
                }
            }
        }
    }

    // TODO: 2022-05-13 和新模式相关的取消充电需要修改
    public JSONObject cancelCharge(long vehicleId) throws InvalidOperateError, BillNotFoundError, QueueNotFoundError, PileNotFoundError, ServiceNotSupport, WithoutAuthorityError {
        // 先判断是否存在于等候区中
        JSONObject jsonObject = new JSONObject();
        if (waitArea.cancelCharge(vehicleId)) {
            // 将车辆设定为未充电
            vehicleRepository.setVehicleState(false, vehicleId);
            // 如果成功移除说明在等候区中
            jsonObject.put("message", "等候区的车辆已取消充电");
            return jsonObject;
        }

        long pileId = getPileIdByVehicleId(vehicleId);
        ChargeRequest request = getRequestByVehicleId(vehicleId);

        long queueId = pileToQueue.get(pileId);
        if (!waitQueue.containsKey(queueId)) {
            throw new QueueNotFoundError();
        }
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        // 没有车辆在充电，自然无法取消
        if (thisQueue.getChargingVehicle() == null) {
            throw new InvalidOperateError();
        }
        QueueStateEnum queueStateEnum = thisQueue.getState();
        int chargeTime = thisQueue.getChargingVehicle().getEstimateChargeTime();
        // 将车辆设置为未在充电

        if (thisQueue.getChargingVehicle().getVehicleId() == vehicleId) {
            if (queueStateEnum == QueueStateEnum.ILLEGAL_OCCUPATION) {
                //如果是非法占用
                MessageItem messageItem = new MessageItem(request.getCustomerId(), "因车位被非法占用，本次充电请求不收取您服务费");
                messageItemRepository.saveAndFlush(messageItem);
                thisQueue.cancelChargeTime();
                billService.endBill(vehicleId);
                if (thisQueue.getWaitLength() != 0) {
                    thisQueue.nextVehicle();
                }
                // 从等候区获取一辆车
                nextRequest(thisQueue.isFast(), pileId);
                // 将车辆设置为未在充电
                vehicleRepository.setVehicleState(false, vehicleId);
            } else if (queueStateEnum == QueueStateEnum.WAITING_DRIVE_IN) {
                // 更新队列状态
                thisQueue.cancelChargeState();
                // 更新等待队列中车辆的等待时间
                thisQueue.cancelChargeTime();
                billService.endBill(vehicleId);
                long nextVehicleId = thisQueue.nextVehicle();
                if (nextVehicleId != -1) {
                    MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "您前方的车辆已取消预约，请您及时驶入准备充电");
                    messageItemRepository.saveAndFlush(messageItem);
                    // TODO: 2022-06-15 tempChange
                    driveIn(nextVehicleId);
                    plugInPile(nextVehicleId);
                }
                if (baseStrategyEnum == BaseStrategyEnum.SINGLE) {
                    // 从等候区获取一辆车
                    nextRequest(thisQueue.isFast(), pileId);
                }
                // 将车辆设置为未在充电
                vehicleRepository.setVehicleState(false, vehicleId);
            } else if (queueStateEnum == QueueStateEnum.WAITING_PLUG_IN) {
                MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "您已取消充电，请及时驶离车位");
                messageItemRepository.saveAndFlush(messageItem);
                thisQueue.cancelChargeState();
                thisQueue.cancelChargeTime();
                billService.endCharge(vehicleId);
                // TODO: 2022-06-15 tempChange
                driveAway(vehicleId);
            } else if (queueStateEnum == QueueStateEnum.CHARGING) {
                MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "您正在充电,已为您取消充电，请及时驶离车位");
                messageItemRepository.saveAndFlush(messageItem);
                // 正在充电
                thisQueue.cancelChargeState();
                thisQueue.cancelChargeTime();
                billService.endCharge(vehicleId);

                // TODO: 2022-06-15 tempChange
                unplugPile(vehicleId);
                driveAway(vehicleId);
            }
            jsonObject.put("message", "车辆移除成功");
            return jsonObject;
        } else {
            // 不在队头
            boolean hasRemoved = thisQueue.removeVehicle(vehicleId);
            if (hasRemoved) {
                // 从等候区获取一辆车
                nextRequest(thisQueue.isFast(), pileId);
                jsonObject.put("message", "取消充电，车辆已移除");
                vehicleRepository.setVehicleState(false, vehicleId);
            } else {
                jsonObject.put("message", "车辆不存在等候区和充电区中");
            }
        }
        return jsonObject;
    }

    public JSONObject plugInPile(long vehicleId) throws InvalidOperateError, WithoutAuthorityError, BillNotFoundError, QueueNotFoundError {
        System.out.println("已插入：" + vehicleId);
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }

        long queueId = pileToQueue.get(pileId);
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        // 没有车辆在充电，自然无法拔出||车辆和请求不符
        if (thisQueue.getChargingVehicle() == null || thisQueue.getChargingVehicle().getVehicleId() != vehicleId) {
            throw new InvalidOperateError();
        }
        if (thisQueue.getState() != QueueStateEnum.WAITING_PLUG_IN) {
            throw new WithoutAuthorityError();
        }
        thisQueue.startCharge();
        // 计费
        billService.startCharge(vehicleId);
        jsonObject.put("message", "插入成功，开始充电");
        return jsonObject;
    }

    public JSONObject unplugPile(long vehicleId) throws InvalidOperateError, BillNotFoundError, QueueNotFoundError {
        System.out.println("已拔出：" + vehicleId);
        JSONObject jsonObject = new JSONObject();
        // 拔出充电桩
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }
        long queueId = pileToQueue.get(pileId);
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        // 没有车辆在充电，自然无法拔出||车辆和请求不符
        if (thisQueue.getChargingVehicle() == null || thisQueue.getChargingVehicle().getVehicleId() != vehicleId) {
            throw new InvalidOperateError();
        }
        // 队列的状态为等待拔出
        if (thisQueue.getState() == QueueStateEnum.WAITING_PLUG_OFF) {
            thisQueue.unplug();
            jsonObject.put("message", "车辆充电头已成功拔出");
            billService.endCharge(vehicleId);
        } else if (thisQueue.getState() == QueueStateEnum.CHARGING) {
            // 结算
            billService.endCharge(vehicleId);
            // 给用户发通知，下次不要这样了
            thisQueue.unplug();
            MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "您在车辆充电中拔出了充电头，这很可能会损害您的车辆，请避免此类行为");
            messageItemRepository.saveAndFlush(messageItem);
            jsonObject.put("message", "车辆正在充电中，充电头被强行拔出");
        } else {
            throw new InvalidOperateError();
        }
        return jsonObject;
    }

    /*
        因为没有实际的充电桩发送驶离信息
        因此用接口来模拟车辆的驶入和驶离
        实际部署时应该是由充电桩或者其他红外识别设备来识别车辆是否驶离
     */
    public JSONObject driveIn(long vehicleId) throws InvalidOperateError {
        System.out.println("已驶入：" + vehicleId);
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }

        long queueId = pileToQueue.get(pileId);

        WaitQueue thisQueue = waitQueue.get(queueId);
        if (thisQueue.getState() == QueueStateEnum.FREE) {
            thisQueue.driveIn(vehicleId);
            //  报警
            Alert alert = new Alert(AlertTypeEnum.ILLEGAL_OCCUPATION, "空闲的充电桩：" + pileId + " 被车辆" + vehicleId + "非法驶入");
            alertRepository.saveAndFlush(alert);
            jsonObject.put("message", "空闲车位被占据");
            return jsonObject;
        } else if (thisQueue.getState() == QueueStateEnum.WAITING_DRIVE_IN) {
            thisQueue.driveIn(vehicleId);
            if (thisQueue.getChargingVehicle().getVehicleId() == vehicleId) {
                jsonObject.put("message", "成功驶入");
            } else {
                // 报警
                Alert alert = new Alert(AlertTypeEnum.ILLEGAL_OCCUPATION, "已被预约的充电桩：" + pileId + " 被车辆" + vehicleId + "非法驶入");
                alertRepository.saveAndFlush(alert);
                jsonObject.put("message", "预约车位被非法驶入");
            }
            return jsonObject;
        } else {
            // 车位都不是空的，我看你怎么驶入？
            throw new InvalidOperateError();
        }
    }

    public JSONObject driveAway(long vehicleId) throws BillNotFoundError, QueueNotFoundError, InvalidOperateError, PileNotFoundError, ServiceNotSupport, WithoutAuthorityError {
        // TODO: 2022-06-07 空闲车辆驶离bug
        System.out.println("已驶离：" + vehicleId);

        // 驶离停车位
        JSONObject jsonObject = new JSONObject();
        ChargeRequest request = getRequestByVehicleId(vehicleId);
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }

        long queueId = pileToQueue.get(pileId);
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));

        if (thisQueue.getState() == QueueStateEnum.ILLEGAL_OCCUPATION && thisQueue.getChargingVehicle() == null) {
            // 非法车辆在空闲状态下驶离
            thisQueue.driveAway();
        } else if (thisQueue.getState() == QueueStateEnum.ILLEGAL_OCCUPATION && thisQueue.getChargingVehicle() != null) {
            thisQueue.driveAway();
            // 启动通知
            thisQueue.nextVehicle();
            MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "占用您车位的车辆已经驶离，请您及时驶入");
            messageItemRepository.saveAndFlush(messageItem);
        } else {
            if (thisQueue.getChargingVehicle() == null || thisQueue.getChargingVehicle().getVehicleId() != vehicleId) {
                throw new InvalidOperateError();
            }
            // 下面都是合法操作
            // 把队列数据库中的等待时间和车辆数都减1
            int chargeTime = thisQueue.getChargingVehicle().getEstimateChargeTime();
            // 将车辆设置为未在充电
            vehicleRepository.setVehicleState(false, vehicleId);
            if (thisQueue.getState() == QueueStateEnum.WAITING_PLUG_IN) {
                // 等待插入的时候车辆直接开走，我们认为是取消充电了
                thisQueue.cancelChargeState();
            } else if (thisQueue.getState() == QueueStateEnum.WAITING_DRIVE_AWAY) {
                MessageItem messageItem = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), "检测到您的车辆已驶离，祝您旅途愉快");
                messageItemRepository.saveAndFlush(messageItem);
                thisQueue.driveAway();
                if (thisQueue.getWaitLength() != 0) {
                    long nextVehicleId = thisQueue.nextVehicle();
                    //  通知
                    String messageContent = "车位已空闲，请在规定时间内启动充电流程";
                    MessageItem nextMessage = new MessageItem(thisQueue.getChargingVehicle().getCustomerId(), messageContent);
                    messageItemRepository.saveAndFlush(nextMessage);

                    // TODO: 2022-06-15 tempChange
                    driveIn(nextVehicleId);
                    plugInPile(nextVehicleId);
                }
            }
            if (baseStrategyEnum == BaseStrategyEnum.SINGLE) {
                // 从等候区中拉个新车来到队列里
                nextRequest(thisQueue.isFast(), pileId);
            }
//            nextRequest(thisQueue.isFast(), pileId);
            // 账单记录
            Bill thisBill = billService.endBill(vehicleId);
            // 将车辆设置为未在充电
            vehicleRepository.setVehicleState(false, vehicleId);
            // 给充电桩增加充电次数
            chargePileRepository.addWorkTimes(pileId);
            // 给充电桩增加充电总时长(充电费+服务费)
            chargePileRepository.addBill(pileId, thisBill.getChargeBill(), thisBill.getServiceBill());
            // 增加总充电量
            double addQuantity = (double) thisBill.getChargeMinutes() * thisBill.getChargePower() / 60;
            chargePileRepository.addChargeQuantity(pileId, addQuantity);
        }
        jsonObject.put("message", "车辆已驶离，祝您一路顺风");
        return jsonObject;
    }

    public void finishCharge(long vehicleId) throws BillNotFoundError, QueueNotFoundError, InvalidOperateError, PileNotFoundError, ServiceNotSupport, WithoutAuthorityError {
        unplugPile(vehicleId);
        driveAway(vehicleId);
    }

    // 做一个复用，从等候区获取车辆调用这个方法
    // 注意，是从等候区取车辆到等候队列中，不是直接去充电
    // 应该是在等候队列有空闲的时候调用（比如有车辆开始充电了或者取消了）
    private void nextRequest(boolean isFast, long pileId) throws PileNotFoundError, ServiceNotSupport {
        // 这个if好像可以省略，当故障策略不是优先级调度的时候，也不会存在errorList
        if (errorStrategyEnum == ErrorStrategyEnum.PRIORITY) {
            if (errorList.size() != 0) {
                ChargeRequest chargeRequest = errorList.pollFirst();
                chargeRequest.setSelectPileId(pileId);
                chargeRequest.setEstimateWaitTime(waitQueue.get(pileToQueue.get(pileId)).getTotalWaitTime());
                MessageItem nextMessage = new MessageItem(chargeRequest.getCustomerId(), "由于您因故障暂停充电，已为您启动高优先级调度，您已进入充电区");
                messageItemRepository.saveAndFlush(nextMessage);
                addVehicleToPile(chargeRequest);
                return;
            }
        }
        ChargeRequest nextRequest = waitArea.getNextRequest(isFast);
        if (nextRequest != null) {
            MessageItem nextMessage = new MessageItem(nextRequest.getCustomerId(), "您已进入充电区,请耐心等候");
            messageItemRepository.saveAndFlush(nextMessage);
            nextRequest.setSelectPileId(pileId);
            nextRequest.setEstimateWaitTime(waitQueue.get(pileToQueue.get(pileId)).getTotalWaitTime());
            addVehicleToPile(nextRequest);
        }
    }


    public List<JSONObject> getAllVehicleInfo(long customerId) {
        List<JSONObject> resultList = new LinkedList<>();
        List<Long> vehicleIdList = vehicleRepository.getChargingVehicle(customerId);
        // 遍历每一个队列
        for (Long vehicleId : vehicleIdList) {
            resultList.add(getSigVehicleInfo(vehicleId));
        }
        return resultList;
    }

    public JSONObject getSigVehicleInfo(long vehicleId) {
        ChargeRequest areaRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (areaRequest != null) {
            // 此车在等候区中，直接返回即可
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("vehicleId", vehicleId);
            jsonObject.put("chargeDetail", areaRequest);
            jsonObject.put("orderNum", areaRequest.getOrderNumber());
            jsonObject.put("vehicleState", "waitingArea");
            jsonObject.put("waitAreaLength", waitArea.countAhead(vehicleId));
            return jsonObject;
        }

        for (WaitQueue waitQueue : waitQueue.values()) {
            if (waitQueue.getChargingVehicle() != null && waitQueue.getChargingVehicle().getVehicleId() == vehicleId) {
                JSONObject jsonObject = new JSONObject();
                ChargeRequest chargeRequest = waitQueue.getChargingVehicle();
                jsonObject.put("orderNum", chargeRequest.getOrderNumber());
                jsonObject.put("chargeDetail", chargeRequest);
                jsonObject.put("vehicleState", waitQueue.getState());
                return jsonObject;
            }
            if (waitQueue.getWaitLength() != 0) {
                LinkedList<ChargeRequest> waitList = waitQueue.getChargeRequestLinkedList();
                int waitTime = waitQueue.getChargingVehicle().getEstimateChargeTime();
                // 遍历队列里的每一个请求
                int queueIndex = 1;
                for (ChargeRequest chargeRequest : waitList) {
                    chargeRequest.setEstimateWaitTime(waitTime);
                    if (chargeRequest.getVehicleId() == vehicleId) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("orderNum", chargeRequest.getOrderNumber());
                        jsonObject.put("chargeDetail", chargeRequest);
                        jsonObject.put("waitQueueLength", queueIndex);
                        jsonObject.put("vehicleState", "waiting");
                        return jsonObject;
                    }
                    // 每次都计算一下等待时间
                    waitTime += chargeRequest.getEstimateChargeTime();
                    queueIndex++;
                }
            }
        }
        return null;
    }

    public Collection<WaitQueue> getAllWaitQueue() {
        return waitQueue.values();
    }

    public HashMap<Long, WaitQueue> getWaitQueue() {
        return waitQueue;
    }

    public HashMap<Long, Long> getPileToQueue() {
        return pileToQueue;
    }

    public long getQueueId(long pileId) {
        return this.pileToQueue.get(pileId);
    }

    public boolean containsPile(long pileId) {
        return pileToQueue.containsKey(pileId);
    }

    public int getAvgPreMinutes() {
        return avgPreMinutes;
    }

    public int getAvgAfterMinutes() {
        return avgAfterMinutes;
    }

    public int getOrder() {
        nowOrderNumber++;
        return nowOrderNumber;
    }

    // 找出最短的那个队列，所有队列都没有位置的话就返回空
    private WaitQueue findShortestQueue(boolean fast) {
        WaitQueue result = null;
        for (WaitQueue waitQueue : waitQueue.values()) {
            if (!waitQueue.isFull() && waitQueue.isFast() == fast) {
                if (result == null || waitQueue.getTotalWaitTime() < result.getTotalWaitTime()
                        || (waitQueue.getTotalWaitTime() == result.getTotalWaitTime() && waitQueue.getChargingVehicle() == null)) {
                    result = waitQueue;
                }
            }
        }
        return result;
    }

    public void setBaseStrategyEnum(BaseStrategyEnum baseStrategyEnum) {
        this.baseStrategyEnum = baseStrategyEnum;
    }

    public void setErrorStrategyEnum(ErrorStrategyEnum errorStrategyEnum) {
        this.errorStrategyEnum = errorStrategyEnum;
    }

    public boolean resetCapacity(long vehicleId, NewCapacity newCapacity) {
        ChargeRequest chargeRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (chargeRequest == null) {
            return false;
        }
        chargeRequest.changeCapacity(newCapacity);
        return true;
    }

    public boolean resetPatten(long vehicleId, boolean isFast) {
        ChargeRequest chargeRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (chargeRequest == null) {
            return false;
        }
        chargeRequest.setFastCharge(isFast);
        chargeRequest.setOrderNumber(getOrder());
        waitArea.moveToTail(vehicleId, isFast);
        return true;
    }

    // 改变队列最大长度
    public void shrinkQueue(int newLength) {
        if (newLength >= MAX_QUEUE_LENGTH) {
            MAX_QUEUE_LENGTH = newLength;
            return;
        }
        MAX_QUEUE_LENGTH = newLength;
        for (WaitQueue queue : waitQueue.values()) {
            if (queue.getWaitLength() > newLength) {
                LinkedList<ChargeRequest> requestList = queue.getChargeRequestLinkedList();
                while (requestList.get(newLength - 1) != null) {
                    ChargeRequest removedRequest = requestList.remove(newLength);
                    waitArea.insertRequest(removedRequest);
                }
            }
        }
        WaitQueue.queueSize = newLength;
    }

    // 改变等候区大小
    public void shrinkArea(int newSize) {
        // 如果大于等于的话则直接忽略，其他情况下可能得移除部分车辆
        // 公平起见得把快充和慢充的车辆按照OrderNum排个序
        if (MAX_AREA_SIZE <= newSize) {
            MAX_AREA_SIZE = newSize;
            return;
        }
        MAX_AREA_SIZE = newSize;
        waitArea.removeTail(newSize);
        waitArea.setMAX_AREA_SIZE(MAX_AREA_SIZE);
    }

    private ChargeRequest getRequestByVehicleId(long vehicleId) {
        for (WaitQueue queue : waitQueue.values()) {
            ChargeRequest targetRequest = queue.getRequestById(vehicleId);
            if (targetRequest != null) {
                return targetRequest;
            }
        }
        return null;
    }

    private long getPileIdByVehicleId(long vehicleId) {
        ChargeRequest request = getRequestByVehicleId(vehicleId);
        long pileId = 0;
        if (request != null) {
            pileId = request.getSelectPileId();
        }
        return pileId;
    }

    public void setMIN_SIG_SIZE(int newSize) {
        this.MIN_SIG_SIZE = newSize;
    }

    public int getMAX_QUEUE_LENGTH() {
        return MAX_QUEUE_LENGTH;
    }

    public int getMAX_AREA_SIZE() {
        return MAX_AREA_SIZE;
    }

    public int getMIN_SIG_SIZE() {
        return MIN_SIG_SIZE;
    }

    public BaseStrategyEnum getBaseStrategyEnum() {
        return baseStrategyEnum;
    }

    public ErrorStrategyEnum getErrorStrategyEnum() {
        return errorStrategyEnum;
    }

    public WaitAreaMonitor waitAreaMonitor() {
        return new WaitAreaMonitor(waitArea.fastSize(), waitArea.slowSize());
    }


}
